#include <crt_util.h>

wchar_t* s_wstrcpy(wchar_t* dst, size_t size, wchar_t const* src)
{
    wchar_t* p1 = dst;
    wchar_t const* p2 = src;

    size_t available = size - 1;
    while ((*p1++ = *p2++) != 0 && --available > 0) { }
    return (dst);
}

wchar_t* s_wstrncpy(wchar_t* dst, const wchar_t* src, size_t size)
{
    wchar_t* start = dst;

    size_t count = size;

    while (count && (*dst++ = *src++)) /* copy string */
        count--;

    if (count) /* pad out with zeroes */
        while (--count)
            *dst++ = L'\0';

    return (start);
}

wchar_t* s_wstrcat(wchar_t* dst, size_t size, const wchar_t* src)
{
    s_wstrcpy(dst + s_wstrlen(dst), size, src);
    return dst;
}

wchar_t* s_wstrncat(wchar_t* front, const wchar_t* back, size_t count)
{
    wchar_t* ret = front;

    /* Find the end of dest.  */
    front += s_wstrlen(front);

    size_t ds = s_wstrnlen(back, count);

    front[ds] = L'\0';
    s_wmemcpy(front, back, ds);

    return ret;
}

int s_wstrcmp(const wchar_t* src, const wchar_t* dst)
{
    int ret = 0;

    while (!(ret = (int)(*src - *dst)) && *dst)
        (void)(++src), ++dst;

    if (ret < 0)
        ret = -1;
    else if (ret > 0)
        ret = 1;

    return (ret);
}

int s_wstricmp(const wchar_t* dst, const wchar_t* src)
{
    wchar_t f, l;
    do {
        f = _crt_towlower(*dst);
        l = _crt_towlower(*src);
        dst++;
        src++;
    } while ((f) && (f == l));
    return (int)(f - l);
}

int s_wstrcasecmp(const wchar_t* dst, const wchar_t* src)
{
    wchar_t f, l;
    do {
        f = _crt_towlower(*dst);
        l = _crt_towlower(*src);
        dst++;
        src++;
    } while ((f) && (f == l));
    return (int)(f - l);
}

int s_wstrncmp(const wchar_t* first, const wchar_t* last, size_t count)
{
    wchar_t f, l;
    int result = 0;
    if (count) {
        do {
            f = _crt_towlower(*first);
            l = _crt_towlower(*last);
            first++;
            last++;
        } while ((--count) && f && (f == l));

        result = (int)(f - l);
    }
    return result;
}

wchar_t* s_wstrchr(const wchar_t* string, wchar_t ch)
{
    while (*string && *string != (wchar_t)ch)
        string++;

    if (*string == (wchar_t)ch)
        return ((wchar_t*)string);
    return (NULL);
}

wchar_t* s_wstrrchr(const wchar_t* string, wchar_t ch)
{
    wchar_t* start = (wchar_t*)string;

    while (*string++) /* find end of string */
        ;
    /* search towards front */
    while (--string != start && *string != (wchar_t)ch)
        ;

    if (*string == (wchar_t)ch) /* wchar_t found ? */
        return ((wchar_t*)string);

    return (NULL);
}

size_t s_wstrspn(const wchar_t* string, const wchar_t* control)
{
    wchar_t* str = (wchar_t*)string;
    wchar_t* ctl;

    /* 1st char not in control string stops search */
    while (*str) {
        for (ctl = (wchar_t*)control; *ctl != *str; ctl++) {
            if (*ctl == (wchar_t)0) {
                /*
                 * reached end of control string without finding a match
                 */
                return (size_t)(str - string);
            }
        }
        str++;
    }
    /*
     * The whole string consisted of characters from control
     */
    return (size_t)(str - string);
}

size_t s_wstrcspn(const wchar_t* string, const wchar_t* control)
{
    wchar_t* str = (wchar_t*)string;
    wchar_t* wcset;

    /* 1st char in control string stops search */
    while (*str) {
        for (wcset = (wchar_t*)control; *wcset; wcset++) {
            if (*wcset == *str) {
                return (size_t)(str - string);
            }
        }
        str++;
    }
    return (size_t)(str - string);
}

wchar_t* s_wstrpbrk(const wchar_t* string, const wchar_t* control)
{
    wchar_t* wcset;

    /* 1st char in control string stops search */
    while (*string) {
        for (wcset = (wchar_t*)control; *wcset; wcset++) {
            if (*wcset == *string) {
                return (wchar_t*)string;
            }
        }
        string++;
    }
    return NULL;
}

wchar_t* s_wstrstr(const wchar_t* wcs1, const wchar_t* wcs2)
{
    wchar_t* cp = (wchar_t*)wcs1;
    wchar_t *s1, *s2;

    if (!*wcs2)
        return (wchar_t*)wcs1;

    while (*cp) {
        s1 = cp;
        s2 = (wchar_t*)wcs2;

        while (*s1 && *s2 && !(*s1 - *s2))
            (void)(s1++), s2++;

        if (!*s2)
            return (cp);

        cp++;
    }

    return (NULL);
}

size_t s_wstrlen(const wchar_t* wcs)
{
    size_t len = 0;

    while (wcs[len] != L'\0') {
        if (wcs[++len] == L'\0')
            return len;
        if (wcs[++len] == L'\0')
            return len;
        if (wcs[++len] == L'\0')
            return len;
        ++len;
    }

    return len;
}

size_t s_wstrnlen(const wchar_t* s, size_t maxlen)
{
    const wchar_t* ret = s_wmemchr(s, L'\0', maxlen);
    if (ret)
        maxlen = ret - s;
    return maxlen;
}

wchar_t* s_wstrtok(wchar_t* string, const wchar_t* control, wchar_t** context)
{
    wchar_t* token;
    const wchar_t* ctl;

    /* If string==NULL, continue with previous string */
    if (!string)
        string = *context;

    /* Find beginning of token (skip over leading delimiters). Note that
     * there is no token iff this loop sets string to point to the terminal
     * null (*string == '\0') */

    while (*string) {
        for (ctl = control; *ctl && *ctl != *string; ctl++)
            ;
        if (!*ctl)
            break;
        string++;
    }

    token = string;

    /* Find the end of the token. If it is not the end of the string,
     * put a null there. */
    for (; *string; string++) {
        for (ctl = control; *ctl && *ctl != *string; ctl++)
            ;
        if (*ctl) {
            *string++ = '\0';
            break;
        }
    }

    /* Update nextoken (or the corresponding field in the per-thread data
     * structure */
    *context = string;

    /* Determine if a token has been found. */
    if (token == string)
        return NULL;
    else
        return token;
}

wchar_t* s_wstrset(wchar_t* string, wchar_t val)
{
    wchar_t* start = string;

    while (*string)
        *string++ = (wchar_t)val;

    return (start);
}

wchar_t* s_wstrrev(wchar_t* string)
{
    wchar_t* start = string;
    wchar_t* left = string;
    wchar_t ch;

    while (*string++) /* find end of string */
        ;
    string -= 2;

    while (left < string) {
        ch = *left;
        *left++ = *string;
        *string-- = ch;
    }

    return (start);
}
